package controller;

import javax.swing.*;
import javax.swing.border.EmptyBorder;



import model.Absorber;
import model.Ball;
import model.Board;
import model.CircleBumper;
import model.FileSaver;
import model.LeftFlipper;
import model.GizmoParser;
import model.SquareBumper;
import model.TriangleBumper;

import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

enum ToggledButton {
	SQUARE,
	CIRCLE,
	TRIANGLE,
	FLIPPER,
	DELETE,
	ROTATE
}

class ApplicationWindow extends JFrame implements KeyListener {

	private AnimationWindow animationWindow;
	Board board;
	Ball ball;
	JFileChooser filechooser = new JFileChooser();
	ToggledButton toggledButton;
	ArrayList<AbstractButton> buttons;

	// global button references not strictly necessary but cleans the code a lot.
	// without it the action listeners would be using
	// ((Component)e.getSource()).getParent().getComponents() and instanceOf()
	
	public ApplicationWindow(Ball ball, Board board) {

		super("GizmoBall");
		this.board = board;
		this.ball = ball;
		toggledButton = null;
		addKeyListener(new MyKeyAction());
		addKeyListener(this);
		
		animationWindow = new AnimationWindow(ball, board);
		JScrollPane scrollPane = new JScrollPane(animationWindow);
		scrollPane.addMouseListener(new PanelMouseListener());
		scrollPane.addMouseMotionListener(new PanelMouseListener());

		JPanel contentPane = new JPanel();
		contentPane.setLayout(new BorderLayout());
		contentPane.add(scrollPane, BorderLayout.CENTER);
		contentPane.add(createButtonPnl(), BorderLayout.EAST);
		
		setJMenuBar(createMenuBar());
		setContentPane(contentPane);
		setResizable(true);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
	}// end of constructor


	protected JMenuBar createMenuBar() {

		FileMenuAction fileMenuListener = new FileMenuAction();
		OptionMenuAction optionMenuListener = new OptionMenuAction ();
		HelpMenuAction helpMenuListener = new HelpMenuAction();
		JMenuBar menuBar = new JMenuBar();

		// file menu entries
		JMenu fileMenu = new JMenu("File");;
		JMenuItem clearItem = new JMenuItem("Clear Board");
		JMenuItem loadItem = new JMenuItem("Load Board");
		JMenuItem saveItem = new JMenuItem("Save Board");
		JMenuItem exitItem = new JMenuItem("Exit");
		fileMenu.add(clearItem);
		fileMenu.add(loadItem);
		fileMenu.add(saveItem);
		fileMenu.add(exitItem);
		clearItem.addActionListener(fileMenuListener);
		loadItem.addActionListener(fileMenuListener);
		saveItem.addActionListener(fileMenuListener);
		exitItem.addActionListener(fileMenuListener);
		menuBar.add(fileMenu);
		
		// option menu entries
		JMenu optionMenu = new JMenu("Options");;
		JCheckBoxMenuItem gravityToggle = new JCheckBoxMenuItem("Gravity", ball.gravityEnabled());
		gravityToggle.addActionListener(optionMenuListener);
		optionMenu.add(gravityToggle);
				
		JMenu boardSize = new JMenu("Board Size");;
		JMenuItem sizeNormal = new JMenuItem("Normal");
		JMenuItem sizeLarge = new JMenuItem("Large");
		JMenuItem sizeVI = new JMenuItem("Visually Impaired");
		sizeNormal.addActionListener(optionMenuListener);
		sizeLarge.addActionListener(optionMenuListener);
		sizeVI.addActionListener(optionMenuListener);
		boardSize.add(sizeNormal);
		boardSize.add(sizeLarge);
		boardSize.add(sizeVI);
		optionMenu.add(boardSize);
		menuBar.add(optionMenu);

		// help menu entries
		JMenu helpMenu = new JMenu("Help");
		JMenuItem usageItem = new JMenuItem("Usage");
		helpMenu.add(usageItem);
		usageItem.addActionListener(helpMenuListener);
		menuBar.add(helpMenu);

		return menuBar;
	}// end of createMenuBar()


	protected JPanel createButtonPnl() {

		ButtonPanelAction buttonPnlListener = new ButtonPanelAction();
		
		JToggleButton modeBtn = new JToggleButton("Play Mode");
		JToggleButton squareBtn = new JToggleButton("Square");
		JToggleButton circleBtn = new JToggleButton("Circle");
		JToggleButton triangleBtn = new JToggleButton("Triangle");
		JToggleButton flipperBtn = new JToggleButton("Flipper");
		JToggleButton rotateBtn = new JToggleButton("Rotate");
		JToggleButton deleteBtn = new JToggleButton("Delete");
		JButton absorberBtn = new JButton("Absorber");
		JButton moveBtn = new JButton("Move");
		JButton connectBtn = new JButton("Connect");
		
		buttons = new ArrayList<AbstractButton>();
		buttons.add(modeBtn);
		buttons.add(squareBtn);
		buttons.add(circleBtn);
		buttons.add(triangleBtn);
		buttons.add(flipperBtn);
		buttons.add(absorberBtn);
		buttons.add(moveBtn);
		buttons.add(rotateBtn);
		buttons.add(connectBtn);
		buttons.add(deleteBtn);
		
		JPanel buttonPnl = new JPanel();
		buttonPnl.setLayout(new BoxLayout(buttonPnl, BoxLayout.Y_AXIS));

		for (AbstractButton each : buttons) {
			each.setMaximumSize(new Dimension(100, 30));
			each.setMinimumSize(new Dimension(100, 30));
			each.setPreferredSize(new Dimension(100, 25));
			each.addActionListener(buttonPnlListener);
			buttonPnl.add(Box.createVerticalGlue());
			buttonPnl.add(each);
		}
		
		buttonPnl.add(Box.createVerticalGlue());
		buttonPnl.add(new JSeparator(SwingConstants.HORIZONTAL), 3);
		buttonPnl.add(new JSeparator(SwingConstants.HORIZONTAL), 14);
		buttonPnl.setBorder(new EmptyBorder(10, 10, 10, 10));
		return buttonPnl;
	}// end of createButtonPnl()


	protected ImageIcon createImageIcon(String name, String description) {
		File iconFile = new File("images/" +name);
		if (iconFile.exists()) {
			return new ImageIcon(iconFile.getPath(), description);
		} else {
			System.err.println("Couldn't find file: " + iconFile);
			return null;
		}
	}

	
	
	//---------------------------------- INNER CLASSES ----------------------------------

	class FileMenuAction implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			if (e.getActionCommand().equalsIgnoreCase("Clear Board"))
				board.clear();
			
			if (e.getActionCommand().equalsIgnoreCase("Load Board")) {
				int returnVal = filechooser.showOpenDialog(ApplicationWindow.this);
				if (returnVal == JFileChooser.APPROVE_OPTION) {
					try {
						File file = filechooser.getSelectedFile();
						board.clear();
						GizmoParser.readFile(board.getL(), file, board);
						
						
//						ApplicationWindow frame = new ApplicationWindow(ball, newBoard); 
//						frame.pack();
//						frame.setVisible(true);
//						if (newBoard.getGizmos().size() > 0) {
//							remove(animationWindow);
//							add(new AnimationWindow(ball, newBoard));
//							setVisible(true);
//						}
					} catch (IOException e1) {
						JOptionPane.showMessageDialog(null, "Can't open this file.", "Error...", JOptionPane.WARNING_MESSAGE);
					} catch (Exception e1){
						JOptionPane.showMessageDialog(null, "Can't open this file.", "Error...", JOptionPane.WARNING_MESSAGE);
					}
				} else System.out.println("Open command cancelled by user.\n");
			}

			
			if (e.getActionCommand().equalsIgnoreCase("Save Board"))
			{
				FileSaver fs = new FileSaver();
				try {
					fs.saveFile(board);
				} catch (FileNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
			
			if (e.getActionCommand().equalsIgnoreCase("Exit"))
				System.exit(0);;
		}
	}
	

	class OptionMenuAction implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			if (e.getActionCommand().equalsIgnoreCase("Gravity")) {
				boolean b = ((JCheckBoxMenuItem)e.getSource()).getState();
				ball.setGravity(b);
			}
//			if (e.getActionCommand().equalsIgnoreCase("Normal")) {
//				remove(animationWindow);
//				board.setL(25);
//				add(new AnimationWindow(ball, board));
//				setVisible(true);
//			}
//			if (e.getActionCommand().equalsIgnoreCase("Large")) {
//				remove(animationWindow);
//				board.setL(35);
//				add(new AnimationWindow(ball, board));
//				setVisible(true);
//			}
//			if (e.getActionCommand().equalsIgnoreCase("Visually Impaired")) {
//				remove(animationWindow);
//				board.setL(45);
//				add(new AnimationWindow(ball, board));
//				setVisible(true);
//			}
		}
	}
	
	
	class HelpMenuAction implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			if (e.getActionCommand().equalsIgnoreCase("Usage"))
				new HelpFrame();
		}
	}
	

	class ButtonPanelAction implements ActionListener {
		public void actionPerformed(ActionEvent e) {

			AbstractButton currentBtn = (AbstractButton)e.getSource();
			deselectOtherButtons(currentBtn);
			
			if (e.getActionCommand().equalsIgnoreCase("Play Mode")) {
				disableOtherButtons(currentBtn);
				currentBtn.setText("Build Mode");
				animationWindow.setMode(true);
			}
			if (e.getActionCommand().equalsIgnoreCase("Build Mode")) {
				enableAllButtons();
				currentBtn.setText("Play Mode");
				animationWindow.setMode(false);
			}
			if (currentBtn.getText().equalsIgnoreCase("Square")) {
				toggledButton = currentBtn.isSelected() ? ToggledButton.SQUARE : null;
			}
			if (e.getActionCommand().equalsIgnoreCase("Circle")) {
				toggledButton = currentBtn.isSelected() ? ToggledButton.CIRCLE : null;
			}
			if (e.getActionCommand().equalsIgnoreCase("Triangle")) {
				toggledButton = currentBtn.isSelected() ? ToggledButton.TRIANGLE : null;
			}
			if (e.getActionCommand().equalsIgnoreCase("Absorber")) {
				for (int i = 0; i <= 19; i++)
				{
					board.addGizmo(new Absorber(ApplicationWindow.this.board.getL(), "new"),i,19); 
				}
				;
			}
			if (e.getActionCommand().equalsIgnoreCase("Flipper")) {
				toggledButton = currentBtn.isSelected() ? ToggledButton.FLIPPER : null;	
			}
			if (e.getActionCommand().equalsIgnoreCase("Move")) {
				;
			}
			if (e.getActionCommand().equalsIgnoreCase("Rotate")) {
				toggledButton = currentBtn.isSelected() ? ToggledButton.ROTATE : null;
			}
			if (e.getActionCommand().equalsIgnoreCase("Connect")) {
				;
			}
			if (e.getActionCommand().equalsIgnoreCase("Delete")) {
				toggledButton = currentBtn.isSelected() ? ToggledButton.DELETE : null;
			}
		}//end of ButtonPanelAction.actionPerformed()

		private void disableOtherButtons(AbstractButton currentBtn) {
			for (AbstractButton b : buttons) {
				if (b.equals(currentBtn))
					continue;
				b.setEnabled(false);
				b.setSelected(false);
			}
			
		}
		
		private void deselectOtherButtons(AbstractButton currentBtn) {
			for (AbstractButton btn : buttons) {
				if (btn.equals(currentBtn))
					continue;
				btn.setSelected(false);
			}
		}
		
		private void enableAllButtons() {
			for (AbstractButton btn : buttons) {
				btn.setEnabled(true);
				btn.setSelected(false);
			}
		}
	}// end of ButtonPanelAction()
	
	
	class PanelMouseListener implements MouseListener, MouseMotionListener {
		@Override
		public void mouseClicked(MouseEvent e) {}

		@Override
		public void mouseEntered(MouseEvent e) {}

		@Override
		public void mouseExited(MouseEvent e) {}
			
		@Override
		public void mousePressed(MouseEvent e) {}
			
		@Override
		public void mouseMoved(MouseEvent e) {}
				
		@Override
		public void mouseReleased(MouseEvent e) {
			perform(e);
		}
		
		@Override
		public void mouseDragged(MouseEvent e) {
			perform(e);
		}
				
		protected void perform(MouseEvent e) {
			int L = ApplicationWindow.this.board.getL();
			int x = (e.getX()-1)/L;
			int y = (e.getY()-1)/L;
			
//			if (e.getButton() == MouseEvent.BUTTON3) {
//				board.removeGizmo(x, y);
//			}
//			else
			if (toggledButton != null) {
				switch (toggledButton) {
					case SQUARE:	board.addGizmo(new SquareBumper(L), x, y);
									break;
					case CIRCLE:	board.addGizmo(new CircleBumper(L), x, y);
									break;
					case TRIANGLE:	board.addGizmo(new TriangleBumper(L), x, y);
									break;
					case FLIPPER:	board.addGizmo(new LeftFlipper(L), x, y);
									break;
					case DELETE:	board.removeGizmo(x, y);
									break;
					case ROTATE:	if (board.getGizmo(x, y) != null && e.getID() != MouseEvent.MOUSE_DRAGGED)
										board.getGizmo(x, y).rotateClockwise();
									board.update();
									break;
				}
			}
		
		}//end of perform()
	}// end of PanelMouseListener()


	class MyKeyAction implements KeyListener {
		@Override
		public void keyPressed(KeyEvent e) {System.out.println(e);}
		@Override
		public void keyReleased(KeyEvent e) {System.out.println(e);}
		@Override
		public void keyTyped(KeyEvent e) {System.out.println(e);}
	}
	@Override
	public void keyPressed(KeyEvent e) {System.out.println(e);}
	@Override
	public void keyReleased(KeyEvent e) {System.out.println(e);}
	@Override
	public void keyTyped(KeyEvent e) {System.out.println(e);}
	
	
}// end of Outer Class
